<!doctype html>
<html lang="en">
<head>
    <title>webgl collision check</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            color: #61443e;
            font-family: Monospace;
            font-size: 13px;
            text-align: center;

            background-color: #aaccff;
            margin: 0px;
            overflow: hidden;
        }

        #info {
            color: #ffffff;
            position: absolute;
            top: 0px;
            width: 100%;
            padding: 5px;
        }

        a {

            color: yellow;
        }

        #oldie {
            background: rgb(0, 0, 50) !important;
            color: #fff !important;
        }

    </style>
</head>
<body>

<div id="container"></div>

<script src="http://derekliu.blog.51cto.com/attachment/201205/4479510_1336024306.jpg"></script>

<script src="http://derekliu.blog.51cto.com/attachment/201205/4479510_1336039846.jpg"></script>
<script src="http://derekliu.blog.51cto.com/attachment/201205/4479510_1336039876.jpg"></script>
<script>

if (!Detector.webgl) {

    Detector.addGetWebGLMessage();
    document.getElementById('container').innerHTML = "";

}

var container, stats;

var camera, controls, scene, renderer;

var clock = new THREE.Clock();

var wallArray = ["LeftWall", "RightWall", "FrontWall", "BackWall", "Celling", "Floor"];
//    var wallArray = ["Floor"];
var walls = {
    LeftWall:{Position:new THREE.Vector3(10, 300, -300), texture:"http://derekliu.blog.51cto.com/album/4479510/133601724534.png", Size:new THREE.Vector3(20, 600, 600)},
    RightWall:{Position:new THREE.Vector3(590, 300, -300), texture:"http://derekliu.blog.51cto.com/album/4479510/133601724534.png", Size:new THREE.Vector3(20, 600, 600)},
    FrontWall:{Position:new THREE.Vector3(300, 300, -590), texture:"http://derekliu.blog.51cto.com/album/4479510/133601724534.png", Size:new THREE.Vector3(560, 600, 20)},
    BackWall:{Position:new THREE.Vector3(300, 300, -10), texture:"http://derekliu.blog.51cto.com/album/4479510/133601724534.png", Size:new THREE.Vector3(560, 600, 20)},
    Celling:{Position:new THREE.Vector3(300, 590, -300), texture:"http://derekliu.blog.51cto.com/album/4479510/133601719031.jpg", Size:new THREE.Vector3(560, 20, 560)},
    Floor:{Position:new THREE.Vector3(300, 10, -300), texture:"http://derekliu.blog.51cto.com/album/4479510/133601721149.png", Size:new THREE.Vector3(560, 20, 560)}
};
var cylinderArray = ["LieCylinder", "UpCylinder", "SlantedCylinder"];
var cylinders = {
    LieCylinder:{Position:new THREE.Vector3(150, 25, -300), texture:"http://derekliu.blog.51cto.com/album/4479510/133601723593.png", Size:new THREE.Vector3(50, 50, 900), Rotate:new THREE.Vector3(1.57, 0, 0)},
    UpCylinder:{Position:new THREE.Vector3(300, 300, -300), texture:"http://derekliu.blog.51cto.com/album/4479510/133601723593.png", Size:new THREE.Vector3(40, 40, 900), Rotate:new THREE.Vector3(0, 0, 0)},
    SlantedCylinder:{Position:new THREE.Vector3(400, 300, -200), texture:"http://derekliu.blog.51cto.com/album/4479510/133601723593.png", Size:new THREE.Vector3(60, 60, 900), Rotate:new THREE.Vector3(0.75, 0.75, 0.75)}
};
var ballArray = [
//        {Position:new THREE.Vector3(50, 550, -50), Radius:20, texture:"http://derekliu.blog.51cto.com/album/4479510/133601717824.png", Veloc:new THREE.Vector3(40, -20, 40), Acell:new THREE.Vector3(0, 0, 0)},
//        {Position:new THREE.Vector3(250, 550, -50), Radius:20, texture:"http://derekliu.blog.51cto.com/album/4479510/133601717824.png", Veloc:new THREE.Vector3(30, 10, 0), Acell:new THREE.Vector3(0, 0, 0)},
//        {Position:new THREE.Vector3(450, 550, -50), Radius:20, texture:"http://derekliu.blog.51cto.com/album/4479510/133601717824.png", Veloc:new THREE.Vector3(50, -10, 10), Acell:new THREE.Vector3(0, 0, 0)},
//        {Position:new THREE.Vector3(50, 550, -250), Radius:20, texture:"http://derekliu.blog.51cto.com/album/4479510/133601717824.png", Veloc:new THREE.Vector3(10, 30, 0), Acell:new THREE.Vector3(0, 0, 0)},
//        {Position:new THREE.Vector3(250, 550, -250), Radius:20, texture:"http://derekliu.blog.51cto.com/album/4479510/133601717824.png", Veloc:new THREE.Vector3(30, -10, 10), Acell:new THREE.Vector3(0, 0, 0)},
//        {Position:new THREE.Vector3(450, 550, -250), Radius:20, texture:"http://derekliu.blog.51cto.com/album/4479510/133601717824.png", Veloc:new THREE.Vector3(10, 10, 30), Acell:new THREE.Vector3(0, 0, 0)},
    {Position:new THREE.Vector3(50, 550, -450), Radius:20, texture:"http://derekliu.blog.51cto.com/album/4479510/133601717824.png", Veloc:new THREE.Vector3(60, 20, 40), Acell:new THREE.Vector3(0, 0, 0)},
    {Position:new THREE.Vector3(250, 550, -450), Radius:20, texture:"http://derekliu.blog.51cto.com/album/4479510/133601717824.png", Veloc:new THREE.Vector3(20, -60, 40), Acell:new THREE.Vector3(0, 0, 0)},
    {Position:new THREE.Vector3(450, 450, -450), Radius:20, texture:"http://derekliu.blog.51cto.com/album/4479510/133601717824.png", Veloc:new THREE.Vector3(40, -50, 50), Acell:new THREE.Vector3(0, 0, 0)}
];
var objects = [];

init();
animate();

function init() {

    container = document.getElementById('container');

    scene = new THREE.Scene();
    //scene.fog = new THREE.FogExp2( 0xaaccff, 0.0007 );

    camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 20000);
    camera.position.x = 0;
    camera.position.y = 50;
    camera.position.z = 200;
    scene.add(camera);

    controls = new THREE.FirstPersonControls(camera);
    controls.movementSpeed = 300;
    controls.lookSpeed = 0.01;
    controls.lon = 300;

    scene.add(new THREE.AmbientLight(0xffffff));

    var wall, materials, wallobject;
    for (var i = 0; i < wallArray.length; i++) {
        eval("wall = walls." + wallArray[i] + ";");
        //materials = new THREE.MeshLambertMaterial({ ambient:0xffffff, transparent:true, map:THREE.ImageUtils.loadTexture(wall.texture) });
        materials = new THREE.MeshBasicMaterial( { color: 0xffffff, wireframe: true, transparent: true, opacity: 0.1 } );

        wallobject = new THREE.Mesh(new THREE.CubeGeometry(wall.Size.x, wall.Size.y, wall.Size.z, wall.Size.x / 10, wall.Size.y / 10, wall.Size.z / 10), materials);
        wallobject.position.set(wall.Position.x, wall.Position.y, wall.Position.z);
        wallobject.doubleSided = true;
        scene.add(wallobject);
        wall.object = wallobject;
        objects.push(wallobject);
    }
//        var cylinder, cylinderobject;
//        for (var i = 0; i < cylinderArray.length; i++) {
//            eval("cylinder = cylinders." + cylinderArray[i] + ";");
//            materials = new THREE.MeshLambertMaterial({ ambient:0xffffff, map:THREE.ImageUtils.loadTexture(cylinder.texture) });
//
//            cylinderobject = new THREE.Mesh(new THREE.CylinderGeometry(cylinder.Size.x, cylinder.Size.y, cylinder.Size.z, cylinder.Size.x, cylinder.Size.z, false), materials);
//            cylinderobject.position.set(cylinder.Position.x, cylinder.Position.y, cylinder.Position.z);
//            cylinderobject.rotation.set(cylinder.Rotate.x, cylinder.Rotate.y, cylinder.Rotate.z);
//            scene.add(cylinderobject);
//            cylinder.object = cylinderobject;
//            objects.push(cylinderobject);
//        }
    var ball, ballObject;
    for (var i = 0; i < ballArray.length; i++) {
        ball = ballArray[i];
        materials = new THREE.MeshLambertMaterial({ ambient:0xffffff, map:THREE.ImageUtils.loadTexture(ball.texture) });

        ballObject = new THREE.Mesh(new THREE.SphereGeometry(ball.Radius, 64, 32), materials);
        ballObject.position.set(ball.Position.x, ball.Position.y, ball.Position.z);
        scene.add(ballObject);
        ball.object = ballObject;
    }

    var object = new THREE.AxisHelper();
    object.position.set(0, 0, 0);
    object.scale.x = object.scale.y = object.scale.z = 2;
    scene.add(object);

    renderer = new THREE.WebGLRenderer({ clearColor:0x000000, clearAlpha:1 });
    renderer.setSize(window.innerWidth, window.innerHeight);

    container.innerHTML = "";

    container.appendChild(renderer.domElement);

    stats = new Stats();
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.top = '0px';
    container.appendChild(stats.domElement);
}

function animate() {

    requestAnimationFrame(animate);

    render();
    stats.update();

}

function setBallPosition(ball, deltaTime) {
//        console.log("old ball.Position=("+ball.Position.x+","+ball.Position.y+","+ball.Position.z+"),Vec=("+ball.Veloc.x+","+ball.Veloc.y+","+ball.Veloc.z+")");
//        if(ball.Position.y < - ball.Veloc.y + 20){
//            console.log("break;");
//        }
    var ray = new THREE.Ray(ball.Position.clone(), ball.Veloc.clone().normalize());
    var intersects = ray.intersectObjects(objects);

    if (intersects.length > 0) {
        var intersectsTemp = [];
        for(var i = 0;i<intersects.length;i++){
            var has = false;
            var normal = intersects[i].face.normal.clone().normalize();
            if(normal.clone().dot(ball.Veloc.clone().normalize()) > 0){
                has = true;
            }
            for(var j=0;j<intersectsTemp.length;j++){
                if(intersectsTemp[j].face.normal.x == intersects[i].face.normal.x && intersectsTemp[j].face.normal.y == intersects[i].face.normal.y && intersectsTemp[j].face.normal.z == intersects[i].face.normal.z){
                    has = true;
                }
                if(intersectsTemp[j].distance != intersects[i].distance){
                    has = true;
                }
            }
            if(!has){
                intersectsTemp.push(intersects[i]);
            }
        }
        intersects = intersectsTemp;

        var intersect = intersects[ 0 ];
        var distance = intersect.distance;
        var point = intersect.point;
        var distanceNew = ((ball.Veloc.clone().multiplyScalar(deltaTime)).addSelf(ball.Acell.clone().multiplyScalar(deltaTime * deltaTime / 2))).distanceTo(ball.Position.clone());

        if (distance - ball.Radius < distanceNew) {
            var vl = ball.Veloc.clone().length();
            var vtpow2 = 2 * ball.Acell.clone().dot(ball.Veloc.clone().normalize()) * (distance - ball.Radius) + vl * vl;
            var vt;
            if (vtpow2 > 0) {
                vt = Math.sqrt(vtpow2);
            } else {
                vt = 0;
            }

            var vi = ball.Veloc.clone().normalize();
            var vri = new THREE.Vector3(0,0,0);
            var vria = vi.clone();
            for(var i = 0;i<intersects.length;i++){
                var normal = intersects[i].face.normal.clone().normalize();
                if(normal.clone().dot(vi.clone()) > 0){
                    continue;
                }
                var normali = normal.clone().multiplyScalar(-2 * normal.clone().dot(vria.clone()));
                vri = normali.addSelf(vria.clone()).normalize();
                vria = vri.clone();
//                    console.log("Cylinder=("+intersects[i].object.rotation.x+","+intersects[i].object.rotation.y+","+intersects[i].object.rotation.z+")");
            }

            var vr = vri.clone().multiplyScalar(vt);
            //console.log("Vl=" + vl + ",Vt=" + vt + ",Vr=" + vr.clone().length() + ",distance=" + distance);

            ball.Veloc = vr;

            ball.Position = point.clone().addSelf(vri.multiplyScalar(1));
            var ray1 = new THREE.Ray(ball.Position.clone(), ball.Veloc.clone().normalize());
            var intersects1 = ray1.intersectObjects(objects);
            if(intersects1.length > 0){
                var intersect1 = intersects1[ 0 ];
                var distance1 = intersect1.distance;
                var distanceNew1 = ball.Veloc.clone().length() * deltaTime;
                if (distance1 - ball.Radius < distanceNew1) {
                    setBallPosition(ball, deltaTime);
                }else{
                    ball.Position = point.clone().addSelf(vri.multiplyScalar(ball.Radius));
                }
            }else{
                ball.Position = point.clone().addSelf(vri.multiplyScalar(ball.Radius));
            }
        } else {
            ball.Position.addSelf((ball.Veloc.clone().multiplyScalar(deltaTime)).addSelf(ball.Acell.clone().multiplyScalar(deltaTime * deltaTime / 2)));
            ball.Veloc.addSelf(ball.Acell.clone().multiplyScalar(deltaTime));
        }
    } else {
        ball.Position.addSelf((ball.Veloc.clone().multiplyScalar(deltaTime)).addSelf(ball.Acell.clone().multiplyScalar(deltaTime * deltaTime / 2)));
        ball.Veloc.addSelf(ball.Acell.clone().multiplyScalar(deltaTime));
    }
//        console.log("new ball.Position=("+ball.Position.x+","+ball.Position.y+","+ball.Position.z+"),Vec=("+ball.Veloc.x+","+ball.Veloc.y+","+ball.Veloc.z+")");
    ball.object.position.set(ball.Position.x, ball.Position.y, ball.Position.z);
}

function render() {

    var delta = clock.getDelta(),
            time = clock.getElapsedTime() * 10;

    controls.update(delta);
    var deltaTime = 0.5;
    for (var i = 0; i < ballArray.length; i++) {
        var ball = ballArray[i];
//            ball.Position.set(30,30,-100);
//            ball.Veloc.set(-80,-70,0);
        setBallPosition(ball, deltaTime);
    }
    renderer.render(scene, camera);
}

</script>

</body>
</html>
